﻿using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace GeneralizedEnum.UnitTest
{
    public class Status : Enumerated<Status, string>
    {
        // These count as values.
        public static readonly Status Open = new Status("open", "O");
        public static readonly Status Closed = new Status("closed", "C");
        public static readonly Status Pending = new Status("pending", "P");

        // These things should not count as values.
        public string Dummy { get; set; }
        public static readonly string Another = "Test";
        public static Status SomethingElse { get; private set; } // Not a field.
        public Status YetAnother { get; private set; } // Not static
        protected static readonly Status NotInitialized = new Status("x", "X"); // Not public


        private Status(string label, string value) : base(label, value) { }

        // Needed for XML serialization.
        private Status() { }
    }

    [TestClass]
    public class TestEnumerated
    {
        [TestMethod]
        public void TestString()
        {
            var status = Status.Open;
            Assert.AreEqual(status.ToString(), "open");
            Assert.AreEqual(status.Label, status.ToString());
        }

        [TestMethod]
        public void TestEnumerating()
        {
            var values = Status.Values;
            Assert.AreEqual(values.Count(), 3);
            Assert.AreEqual(values.ElementAt(0), Status.Open);
            Assert.AreEqual(values.ElementAt(1), Status.Closed);
        }

        [TestMethod]
        public void TestNameAutoFilled()
        {
            var x = Status.Closed;
            Assert.AreEqual(x.Name, "Closed");
        }

        [TestMethod]
        public void TestEquality()
        {
            var status = Status.Open;
            var status2 = Status.Open;
            Assert.IsTrue(status == status2);
            Assert.IsTrue(status.Equals(status2));

            // This is what the XML serialization uses.
            Status status3 = (Status)Activator.CreateInstance(typeof(Status), true);
            status3.Assign(Status.Open);
            Assert.IsTrue(status3 == status2);
            Assert.IsTrue(status3.Equals(status2));
        }

        [TestMethod]
        public void TestCast()
        {
            string y = (string)Status.Closed;
            Assert.AreEqual(y, "C");

            Status x = (Status)"P";
            Assert.AreEqual(x, Status.Pending);
        }

        [TestMethod]
        [ExpectedException(typeof(InvalidCastException))]
        public void TestBadCast()
        {
            Status s = (Status)"bad value";
        }

        [TestMethod]
        public void TestXml()
        {
            var serializer = new XmlSerializer(typeof(Status));
            var builder = new StringBuilder();
            using (var writer = new StringWriter(builder))
            {
                serializer.Serialize(writer, Status.Open);
            }
            var str = builder.ToString();
            Assert.AreEqual("<?xml version=\"1.0\" encoding=\"utf-16\"?>\r\n<Status>Open</Status>", str);

            using (var reader = new StringReader(str))
            {
                var obj = serializer.Deserialize(reader);
                Assert.AreEqual(obj, Status.Open);
            }
        }

        [TestMethod]
        public void TestIn()
        {
            Assert.IsTrue(Status.Open.In(Status.Open, Status.Closed));
            Assert.IsTrue(Status.Open.In(new Status[] { Status.Open, Status.Closed }));
            Assert.IsFalse(Status.Open.In(Status.Closed, Status.Pending));
            Assert.IsFalse(Status.Open.In(new Status[] { Status.Closed, Status.Pending }));
        }
    }
}
